﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO.Ports;

namespace Common.GSMLibrary 
{
    public enum MsgType { AUSC2, A7Bit };//枚举 短信类型 AUSC2 A7Bit：7位编码 （中文用AUSC2，英文都可以 但7Bit能发送160字符，USC2仅70）
    public class GSMModem
    {
        #region  构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="comPort">串口号</param>
        /// <param name="baudRate">波特率</param>
        public GSMModem(string comPort, int baudRate)
        {
            this.msgCenter = string.Empty;
            this.sp = new SerialPort();
            this.sp.PortName = comPort;
            this.sp.BaudRate = baudRate;
            this.sp.ReadTimeout = -1;//读超时时间 发送短信时间的需要
            this.sp.RtsEnable = true;//必须为true 这样串口才能接收到数据
            this.sp.DataReceived += new SerialDataReceivedEventHandler(this.sp_DataReceived);
        }
        #endregion

        #region  私有变量
        private SerialPort sp;
        private string msgCenter = string.Empty;//服务中心号码
        private int newMsgIndex;//新消息序号
        #endregion

        #region 属性
        /// <summary>
        /// 波特率 运行时只读 设备打开状态写入将引发异常
        /// </summary>
        public int BaudRate
        {
            get { return this.sp.BaudRate; }
            set { this.sp.BaudRate = value; }
        }

        /// <summary>
        /// 串口号 运行时只读 设备打开状态写入将引发异常
        /// 提供对串口端口号的访问
        /// </summary>
        public string ComPort
        {
            get { return this.sp.PortName; }
            set { try { this.sp.PortName = value; } catch (Exception ex) { throw ex; } }
        }

        /// <summary>
        /// 设备是否打开
        /// </summary>
        public bool IsOpen
        {
            get { return this.sp.IsOpen; }
        }

        #endregion

        #region  方法
        /// <summary>
        /// 获取短信中心号码
        /// </summary>
        /// <returns></returns>
        public string GetMsgCenterNo()
        {
            string temp = string.Empty;
            if (msgCenter != null && msgCenter.Length != 0)
            {
                return msgCenter;
            }
            else
            {
                temp = this.SendAT("AT+CSCA?");
                if (temp.Substring(temp.Length - 4, 3).Trim() == "OK")
                {
                    return temp.Split('\"')[1].Trim();
                }
                else
                {
                    throw new Exception("获取短信中心失败");
                }
            }
        }

        public string getPhoneNo()
        {
            return SendAT("");
        }
 
        /// <summary>
        /// 发送AT命令
        /// </summary>
        /// <param name="ATCom">AT命令</param>
        /// <returns></returns>
        public string SendAT(string ATCom)
        {
            string str = string.Empty;
            //忽略接收缓冲区内容，准备发送
            this.sp.DiscardInBuffer();
            //注销事件关联，为发送做准备
            this.sp.DataReceived -= this.sp_DataReceived;
            try
            {
                this.sp.Write(ATCom + "\r");
            }
            catch (Exception ex)
            {
                this.sp.DataReceived += this.sp_DataReceived;
                throw ex;
            }

            try
            {
                string temp = string.Empty;
                while ((temp.Trim() != "OK") && (temp.Trim() != "ERROR"))
                {
                    temp = this.sp.ReadLine();
                    str += temp;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                this.sp.DataReceived += this.sp_DataReceived;
            }
            return str;
        }
        
        //public void WaitHangup(string ATCom)
        //{
        //    string str = string.Empty;
        //    //忽略接收缓冲区内容，准备发送
        //    this.sp.DiscardInBuffer();
        //    //注销事件关联，为发送做准备
        //    this.sp.DataReceived -= this.sp_DataReceived;
        //    try
        //    {
        //        this.sp.Write(ATCom + "\r");
        //    }
        //    catch (Exception ex)
        //    {
        //        this.sp.DataReceived += this.sp_DataReceived;
        //        throw ex;
        //    }
        //    try
        //    {
        //        string temp = string.Empty;
        //        while (temp.Trim() != "NO CARRIER")
        //        {
        //            temp = this.sp.ReadLine();
        //            str += temp;
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        throw ex;
        //    }
        //    finally
        //    {
        //        this.sp.DataReceived += this.sp_DataReceived;
        //    }
        //}

        /// <summary>
        /// 发送短信       
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="msg">短信内容</param>
        public bool SendMsg(string phone, string msg)
        {
            string temp = "0011000D91" + this.reverserNumber(phone) + "000801" + this.contentEncoding(msg) + Convert.ToChar(26).ToString();
            string len = this.getLenght(msg);//计算长度

            try
            {
                this.sp.DataReceived -= sp_DataReceived;
                this.sp.Write("AT+CMGS=" + len + "\r");
                this.sp.ReadTo(">");
                this.sp.DiscardInBuffer();
                //事件重新绑定 正常监视串口数据
                this.sp.DataReceived += sp_DataReceived;
                temp = this.SendAT(temp);
                if (temp.Substring(temp.Length - 4, 3).Trim() != "OK")
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch
            {
                return false;
            }
        }

        #region  自定义方法

        //获取短信内容的字节数
        private string getLenght(string txt)
        {
            int i = 0;
            string s = "";
            i = txt.Length * 2;
            i += 15;
            s = i.ToString();
            return s;
        }
        //将手机号码转换为内存编码
        private string reverserNumber(string phone)
        {
            string str = "";
            //检查手机号码是否按照标准格式写，如果不是则补上
            if (phone.Substring(0, 2) != "86")
            {
                phone = string.Format("86{0}", phone);
            }
            char[] c = this.getChar(phone);
            for (int i = 0; i <= c.Length - 2; i += 2)
            {
                str += c[i + 1].ToString() + c[i].ToString();
            }
            return str;
        }

        //汉字解码为16进制
        private string contentEncoding(string content)
        {
            Encoding encodingUTF = System.Text.Encoding.BigEndianUnicode;
            string s = "";
            byte[] encodeByte = encodingUTF.GetBytes(content);
            for (int i = 0; i <= encodeByte.Length - 1; i++)
            {
                s += BitConverter.ToString(encodeByte, i, 1);
            }
            s = string.Format("{0:X2}{1}", s.Length / 2, s);
            return s;
        }

        private char[] getChar(string phone)
        {
            if (phone.Length % 2 == 0)
            {
                return Convert.ToString(phone).ToCharArray();
            }
            else
            {
                return Convert.ToString(phone + "F").ToCharArray();
            }
        }
        #endregion

        /// <summary>
        /// 发送短信 （重载）
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="msg">短信内容</param>
        /// <param name="msgType">短信类型</param>
        public void SendMsg(string phone, string msg, MsgType msgType)
        {
            if (msgType == MsgType.AUSC2)
            {
                SendMsg(phone, msg);
            }
            else
            {
                PDUEncoding pe = new PDUEncoding();
                pe.ServiceCenterAddress = msgCenter;                    //短信中心号码 服务中心地址

                string temp = pe.PDU7BitEncoder(phone, msg);

                int len = (temp.Length - Convert.ToInt32(temp.Substring(0, 2), 16) * 2 - 2) / 2;  //计算长度
                try
                {
                    temp = SendAT("AT+CMGS=" + len.ToString() + "\r" + temp + (char)(26));  //26 Ctrl+Z ascii码
                }
                catch (Exception)
                {
                    throw new Exception("短信发送失败");
                }

                if (temp.Substring(temp.Length - 4, 3).Trim() == "OK")
                {
                    return;
                }

                throw new Exception("短信发送失败");
            }
        }


        /// <summary>
        /// 获取所有信息
        /// </summary>
        /// <returns></returns>
        public string[] GetAllMsg()
        {
            string[] result = new string[255];
            string[] temp = null;
            string tt = string.Empty;

            tt = this.SendAT("AT+CMGL=4");
            if (tt.Substring(tt.Length - 4, 3).Trim() == "OK")
            {
                temp = tt.Split('\r');
            }
            PDUEncoding pe = new PDUEncoding();
            int i = 0;//计数
            foreach (string str in temp)
            {
                if (str != null && str.Length != 0 && str.Substring(0, 2).Trim() != "+C" && str.Substring(0, 2) != "OK" && str.Substring(0, 2) != "AT")
                {
                    result[i] = pe.PDUDecoder(str);
                    i++;
                }
            }
            return result;
        }

        /// <summary>
        /// 取得未读信息列表
        /// </summary>
        /// <returns>未读信息列表（中心号码，手机号码，发送时间，短信内容）</returns>
        public string[] GetUnReadMsg()
        {
            string[] result = new string[255];//存储255条短信，一般手机上面存储的短信少于这个数            
            string[] temp = null;
            string tt = string.Empty;

            tt = this.SendAT("AT+CMGL=0");//读取未读信息
            if (tt.Substring(tt.Length - 4, 3).Trim() == "OK")
            {
                temp = tt.Split('\r');
            }
            PDUEncoding pe = new PDUEncoding();
            int i = 0;//计数
            foreach (string str in temp)
            {
                if (str != null && str.Length != 0 && str.Substring(0, 2).Trim() != "+C" && str.Substring(0, 2) != "OK" && str.Substring(0, 2) != "AT")
                {
                    result[i] = pe.PDUDecoder(str);
                    i++;
                }
            }
            return result;
        }

        /// <summary>
        /// 关闭设备
        /// </summary>
        public void CloseComm()
        {
            try
            {
                this.sp.Close();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 打开设备
        /// </summary>
        public void OpenComm()
        {
            try
            {
                this.sp.Open();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            if (this.sp.IsOpen)
            {
                try
                {
                    this.SendAT("AT+CMGF=0");//选择短消息格式默认为PDU
                    Thread.Sleep(100);
                    this.SendAT("AT+CNMI=2,1");//选择当有新短消息来时提示方式
                    Thread.Sleep(100);
                }
                catch
                {

                }
            }
        }

        /// <summary>
        /// 拨打电话
        /// </summary>
        /// <param name="telNum">电话号码</param>
        public void Call(string telNum)
        {
            try
            {
                this.SendAT("ATD" + telNum + ";");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion

        /// <summary>
        /// 创建事件收到信息的委托
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void OnRecievedHandler(object sender, EventArgs e);

        /// <summary>
        /// 收到短信息事件 OnRecieved
        /// 收到短信将引发此事件
        /// </summary>
        public event OnRecievedHandler GetNewMsg;

        private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string str = this.sp.ReadLine();
            if ((str.Length > 8) && (str.Substring(0, 6) == "+CMTI:"))
            {
                this.newMsgIndex = Convert.ToInt32(str.Split(',')[1]);
                this.GetNewMsg(this, e);
            }
        }
    }
}